/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  pinyin.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2015/12/22
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include "pinyin.h"

#include <core/sync/lock.h>
#include <core/time/time.h>

#include <algorithm>
#include <bitset>
#include <set>

#include "../container/LevelListNode.h"  // LinkListObjectPool
#include "../container/hash_prime.h"
#include "../system/system_util.h"
#include "Rand.h"
#include "string_convert.h"
#include "string_util.h"

#ifndef WIN32
#include <fcntl.h>
#define _countof(x) (sizeof(x) / sizeof((x)[0]))
#define RTL_FIELD_SIZE(type, field) sizeof(reinterpret_cast<type*>(0)->field)
#define _TRUNCATE ((size_t)-1)
#endif

#ifndef BASSERTS
#define BASSERTS  //
#endif
#ifndef BASSERT
#define BASSERT  //
#endif
#ifndef BLOGERR
#define BLOGERR  //
#endif
#ifndef BLOGINFO
#define BLOGINFO  //
#endif
#ifndef BLOGDEBUG
#define BLOGDEBUG  //
#endif
#ifndef BVIEW
#define BVIEW  //
#endif
#ifndef MEM_METRIC
#define MEM_METRIC  //
#endif
#ifndef BS_METRICS
#define BS_METRICS  //
#endif

namespace qb {
namespace base {

inline wchar_t HexCharToNumber(char c) {
  c = ((c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c);
  return (c >= '0' && c <= '9') ? (c - '0')
                                : (c >= 'a' && c <= 'z' ? (c - 'a' + 10) : 0);
}
inline wchar_t HexStrToWChar(const char* hexstr) {
  wchar_t h1 = HexCharToNumber(hexstr[0]) << 12;
  wchar_t h2 = HexCharToNumber(hexstr[1]) << 8;
  wchar_t h3 = HexCharToNumber(hexstr[2]) << 4;
  wchar_t h4 = HexCharToNumber(hexstr[3]);
  return h1 + h2 + h3 + h4;
}
const wchar_t gs_pinyin_array[] = {
#include "pinyin_array.inc"
};

//--------------------------------------------------------------------------
// 此文件的配置变量，更改后需要编译
//--------------------------------------------------------------------------
namespace pinyin {
enum {
  MAX_HANZI_LENGTH = 128,  // GetAllSpellForString所支持的最大汉字字符个数
  MAX_KEY_LENGTH = 32,  // 允许查询的最大key长度
  MAX_PINYIN_LENGTH =
      4096,  // GetAllSpellForString所能构成的全拼简拼序列的最大长度
};
};
//--------------------------------------------------------------------------
// 算法：遍历整数数组所能组成的所有序列
// 规则：从每个数据项中选一个比该数小的整数，组成新的序列
//--------------------------------------------------------------------------
template <int N>
class TraverseCountArray {
  struct CountInfo {
    int pos, count;
  };
  SFixedArray<CountInfo, N> m_count_infos;

 public:
  TraverseCountArray() {}
  ~TraverseCountArray() {}
  void AddCountInfo(int count) {
    CountInfo ci;
    ci.pos = 0;
    ci.count = count;
    m_count_infos.Add(ci);
  }
  void StartTraverse() {
    for (int i = 0; i < m_count_infos.size(); i++) {
      m_count_infos.at(i).pos = 0;
    }
  }
  int GetPos(int index) const { return m_count_infos.at(index).pos; }
  int GetCount() const { return m_count_infos.size(); }
  bool GetNextState() {
    for (int i = m_count_infos.size() - 1; i >= 0; i--) {
      CountInfo& ci = m_count_infos.at(i);
      if (ci.pos + 1 >= ci.count) continue;
      ci.pos++;
      for (int j = i + 1; j < m_count_infos.size(); j++) {
        m_count_infos.at(j).pos = 0;
      }
      return true;
    }
    return false;
  }

 protected:
};
namespace pinyin {
struct HanziInfo {
  SpellManager::tHanzi name;
  SpellManager::tSpellIds ids;
  bool found;
};
}  // namespace pinyin
////////////////////////////////////////////////////////////////////////////////
class SStringBuf {  //
  typedef wchar_t Element;
  typedef const wchar_t* ConstElementPtr;
  enum { MAGIC = 0xABCD };
  class SStringItem {
   public:
    unsigned short m_magic;  // 固定的值，便于纠错和调试
    unsigned short m_id;     // id，序号
    unsigned short m_len;    // 实际字符串的长度
    unsigned short m_bytes;  // 当前类所占据的字节长度
    SStringItem* m_next;     //
    Element m_string[4];     // [m_bytes/4]+4 length
   public:
    const Element* GetString() const { return m_string; }
    static int PredicateSize(int len) {
      // 预估len个字符长度的字符串所需要占据的空间大小，包括SStringItem大小
      // 另外要考虑4字节对齐
      int thissize =
          sizeof(SStringItem) - RTL_FIELD_SIZE(SStringItem, m_string);
      int num = len * sizeof(Element) / 4;
      int size = num * 4 + 4;
      size = size + thissize;
      BASSERTS(size > len, "");
      return size;
    }
    void Construct(ConstElementPtr str, int len, int size, int id) {
      // size 是字节数，len是字符数
      m_magic = MAGIC;
      m_id = id;
      m_len = len;
      m_bytes = size;
      m_next = NULL;
      memcpy(m_string, str, len * sizeof(Element));
      m_string[len] = Element(0);
    }
    void Check(int id, SStringItem* next) {
      BASSERTS(m_magic == MAGIC, "");
      BASSERTS(m_id == id, "");
      BASSERTS(m_len == wcslen(m_string), "");
      BASSERTS(m_next == next, "");
      BASSERTS((char*)this + m_bytes == (char*)next, "");
    }
  };

  class SStringBlock {
   public:
    enum { BLOCK_SIZE = 4096 };

   protected:
    SStringBlock* m_next;
    SStringBlock* m_prev;
    int m_capcity;  // m_buffer 实际所分配的字节数
    int m_really_used;  // 该block所拥有的字符串的总的长度，不包括末尾null
    int m_used;  // 下一个SStringItem的开头指针相对于m_buffer的偏移量
    int m_count;             // 当前拥有的SStringItem的个数
    Element* m_buffer;       // 实际分配的内存
    SStringItem* m_current;  // 记录最近一次加入的SStringItem指针
   public:
    SStringBlock()
        : m_next(nullptr),
          m_prev(nullptr),
          m_capcity(BLOCK_SIZE),
          m_used(0),
          m_really_used(0),
          m_count(0),
          m_current(NULL) {
      m_buffer = new Element[BLOCK_SIZE];
      memset(m_buffer, 0, BLOCK_SIZE * sizeof(Element));
    }
    ~SStringBlock() {
      if (m_buffer != NULL) {
        delete[] m_buffer;
        m_buffer = NULL;
      }
    }
    inline SStringBlock* GetNext() const { return m_next; }
    inline SStringBlock* GetPrev() const { return m_prev; }
    inline void SetNext(SStringBlock* next) { m_next = next; }
    inline void SetPrev(SStringBlock* prev) { m_prev = prev; }
    void reset() {
      m_really_used = 0;
      m_used = 0;
      m_count = 0;
      m_current = nullptr;
      m_buffer[0] = Element(0);  // 只重置第一个字符
    }
    ConstElementPtr AddString(ConstElementPtr str, int len, int size) {
      BASSERTS(m_buffer != NULL, "");
      BASSERTS(m_capcity > 0 && m_used >= 0 && m_used <= m_capcity, "");
      if (m_used + size > m_capcity) return NULL;

      // m_used 是字节数
      Element* ptr = (Element*)((char*)m_buffer + m_used);
      SStringItem* item = (SStringItem*)(ptr);
      item->Construct(str, len, size, m_count);

      BASSERTS(len < size, "");

      if (m_current) m_current->m_next = item;

      m_current = item;
      m_count++;
      m_used += size;
      m_really_used += len * sizeof(Element);

      // DVIEW("Add String:item:%x old:%s str:%s used:%d
      // count:%d",ptr,str,item->GetString(),m_used,size);
      return item->GetString();
    }
    void Check() {
      SStringItem* item = (SStringItem*)m_buffer;
      SStringItem* next = NULL;
      for (int i = 0; i < m_count; i++) {
        next = item->m_next;
        item->Check(i, next);
        item = next;
      }
    }
  };
  typedef qb::base::container::LinkListObjectPool<SStringBlock>
      SStringBlockPool;
  int m_block_size;  // 应该给SStringBlock分配的内存大小
  std::vector<SStringBlock*> m_blocks;  // address increase
  SStringBlockPool m_pool;

 public:
  SStringBuf() : m_block_size(SStringBlock::BLOCK_SIZE) {}
  ~SStringBuf() { Clear(); }
  void Reset() {
    int count = m_blocks.size();
    for (int i = 0; i < count; i++) {
      SStringBlock* block = m_blocks.at(i);
      m_pool.Free(block);
    }
    m_blocks.clear();
  }

  ConstElementPtr AddString(ConstElementPtr str, int len) {
    BASSERTS(str != NULL, "");
    BASSERTS(len < 256,
             "SStringBuf Can not support string whose size is larger than 256");

    SStringBlock* block = NULL;
    if (m_blocks.size() > 0) {
      block = m_blocks.at(m_blocks.size() - 1);
    }

    int item_size = SStringItem::PredicateSize(len);
    ConstElementPtr str_add =
        block ? block->AddString(str, len, item_size) : NULL;
    if (str_add == NULL) {
      block = Alloc();
      m_blocks.push_back(block);
      str_add = block->AddString(str, len, item_size);
    }

    BASSERTS(str_add != NULL, "");
    return str_add;
  }
  int GetBytes() { return m_blocks.size() * m_block_size; }

 private:
  SStringBlock* Alloc() {
    SStringBlock* block = m_pool.Alloc();
    block->reset();
    return block;
  }
  void Clear() {
    int count = m_blocks.size();
    for (int i = 0; i < count; i++) {
      SStringBlock* block = m_blocks.at(i);
      m_pool.Free(block);
    }
    m_blocks.clear();
  }
};

/*
        如何使用?
        PinyinStat用于统计增加的多音字前缀后缀规则的带来的性能改进
        在SpellMatch::GetSpell中,记录每次的生成时间,然后如果出现多音字,那么调用AddText记录相关信息即可
        实例代码如下:
        HighResTime t1, t2;
                if (bfull)
                {
                        HighResTime timer;
                        fullsuc = GetSpellImpl(text, tlen, fullspell, flen,
   true); t1 = timer.elapsed();
                }
                if (bshort)
                {
                        HighResTime timer;
                        shortsuc = GetSpellImpl(text, tlen, shortspell, slen,
   false); t2 = timer.elapsed();
                }
                if (m_flag_debug)
                {
                        PinyinStat::Get(m_spell_manager)->AddLength(fullsuc,t1,
   shortsuc,t2); if (m_multi_pos.size() > 0)
                        {
                                PinyinStat::Get(m_spell_manager)->AddText(text,
   tlen, m_multi_pos);
                        }
                }
*/
class
    PinyinStat {  // 统计QB的多音字信息,生成多音字修正字典,用于AddDefaultPrefixLines
  struct Sentence {
    wstring text;
    wstring word;
    int count;
  };
  struct HanCode {
    wchar_t code;
    int count;
    std::set<int> sentences;

   public:
    bool operator<(const HanCode& hc) const {
      if (count == hc.count) return code < hc.code;
      return count > hc.count;
    }
  };
  typedef map<wstring, int> SentenceIndexMap;
  typedef map<wchar_t, HanCode> WeightMap;
  typedef std::vector<Sentence> SentenceVec;
  WeightMap m_weights;  // 出现的汉字的多音字的权重,其实就是个数
  SentenceIndexMap m_indexes;
  SentenceVec m_sentences;
  SpellManager* m_spell_manager;
  int m_full_length, m_short_length;
  HighResTime m_full_time, m_short_time;
  int m_text_count;

 public:
  static PinyinStat* Get(SpellManager* sm) {
    static PinyinStat* ps = 0;
    if (ps == NULL) ps = new PinyinStat(sm);
    return ps;
  }
  PinyinStat(SpellManager* sm)
      : m_spell_manager(sm),
        m_text_count(0),
        m_full_length(0),
        m_short_length(0),
        m_full_time(0.0),
        m_short_time(0.0) {}
  void AddLength(int flen, HighResTime t1, int slen, HighResTime t2) {
    // 用于记录每次GetSpell时的全拼时间,全拼长度,简拼时间,简拼长度,用于统计性能
    m_text_count++;
    m_full_length += flen;
    m_short_length += slen;
    m_full_time += t1;
    m_short_time += t2;
  }
  void AddText(const wchar_t* str, int tlen, const std::vector<int>& pinyin) {
    // str是原始文本,tlen是长度,pinyin则是检测出现的多音字索引位置
    wstring word;
    wstring text = wstring(str, tlen);
    for (int i = 0; i < (int)pinyin.size(); i++) word.push_back(str[pinyin[i]]);
    int id = AddSentence(text, word);
    for (int i = 0; i < (int)pinyin.size(); i++)
      AddWeight(m_weights, str[pinyin[i]], id);
  }
  void Dump() {
    BVIEW("[PINYIN] flen:%12d %.2f %.2fus slen:%12d %.2f %.2fus  times:%d",
          m_full_length, m_full_length * 1.0 / m_text_count,
          m_full_time.microsec() * 1.0 / m_text_count, m_short_length,
          m_short_length * 1.0 / m_text_count,
          m_short_time.microsec() * 1.0 / m_text_count, m_text_count);
    vector<HanCode> codes;
    for (WeightMap::iterator it = m_weights.begin(); it != m_weights.end();
         ++it) {
      codes.push_back(it->second);
    }
    std::sort(codes.begin(), codes.end());
    for (int i = 0; i < (int)codes.size(); i++) {
      HanCode& hc = codes[i];
      wstring code;
      code.push_back(hc.code);

      WeightMap prefixmap, postmap;
      BVIEW("[PINYIN]%04d\t%s", hc.count,
            W2G(code.c_str(), code.size()).c_str());
      for (set<int>::iterator it = hc.sentences.begin();
           it != hc.sentences.end(); ++it) {
        int id = *it;
        Sentence& st = m_sentences[id];
        UpdateContextMap(id, st.text, st.word, prefixmap, postmap);
      }

      // 将该汉字的所有前缀和后缀都打印出来
      vector<HanCode> prefix, post;
      for (WeightMap::iterator it = prefixmap.begin(); it != prefixmap.end();
           ++it) {
        prefix.push_back(it->second);
      }
      for (WeightMap::iterator it = postmap.begin(); it != postmap.end();
           ++it) {
        post.push_back(it->second);
      }
      std::sort(prefix.begin(), prefix.end());
      std::sort(post.begin(), post.end());
      wstring prefixstr, poststr;
      for (vector<HanCode>::iterator it = prefix.begin(); it != prefix.end();
           ++it) {
        prefixstr.push_back(it->code);
      }
      for (vector<HanCode>::iterator it = post.begin(); it != post.end();
           ++it) {
        poststr.push_back(it->code);
      }
      BVIEW("[PINYIN]\tprefix:%s",
            W2G(prefixstr.c_str(), prefixstr.size()).c_str());
      BVIEW("[PINYIN]\tpost  :%s",
            W2G(poststr.c_str(), poststr.size()).c_str());
      for (set<int>::iterator it = hc.sentences.begin();
           it != hc.sentences.end(); ++it) {
        int id = *it;
        Sentence& st = m_sentences[id];
        wstring temp = L"\t";
        GetCommaSpell(st.word, temp);
        temp += os::fmtstr(L" %d-", st.count);
        temp += st.text;
        BVIEW("[PINYIN]%s", W2G(temp.c_str(), temp.size()).c_str());
      }
    }
  }

 protected:
  void UpdateContextMap(int id, const wstring& text, const wstring& word,
                        WeightMap& prefix, WeightMap& post) {
    for (int i = 0; i < (int)word.size(); i++) {
      for (int k = 0; k < (int)text.size(); k++) {
        if (word[i] != text[k]) continue;
        if (k - 1 >= 0) AddWeight(prefix, text[k - 1], id);
        if (k + 1 < (int)text.size()) AddWeight(post, text[k + 1], id);
      }
    }
  }
  void GetCommaSpell(const wstring& word, wstring& spell) {
    for (int j = 0; j < (int)word.size(); j++) {
      if (j > 0) spell.push_back(';');
      spell.push_back(word[j]);
      spell.push_back(':');
      SpellManager::tSpellIds ids;
      m_spell_manager->GetSpellId(word[j], ids);
      for (int i = 0; i < (int)ids.size(); i++) {
        if (i > 0) spell.push_back(',');
        const SpellManager::SpellInfo& si =
            m_spell_manager->GetSpellInfo(ids[i]);
        spell.append(si.name.data);
      }
    }
  }
  void AddWeight(WeightMap& weights, wchar_t code, int id) {
    WeightMap::iterator it = weights.find(code);
    if (it == weights.end()) {
      HanCode hc;
      hc.code = code;
      hc.count = 1;
      hc.sentences.insert(id);
      weights[code] = hc;
    } else {
      HanCode& hc = weights[code];
      hc.sentences.insert(id);
      hc.count++;
    }
  }
  int AddSentence(const wstring& text, const wstring& word) {
    int id = GetSentenceId(text);
    if (id < 0) {
      Sentence st;
      st.text = text;
      st.word = word;
      st.count = 1;
      m_sentences.push_back(st);
      return m_sentences.size() - 1;
    }
    Sentence& st = m_sentences[id];
    st.count++;
    return id;
  }
  int GetSentenceId(const wstring& text) {
    SentenceIndexMap::iterator it = m_indexes.find(text);
    return it != m_indexes.end() ? it->second : (-1);
  }
};
////////////////////////////////////////////////////////////////////////////////
class SpellMatch {
  struct MatchPos {
    int group;
    int select;
    int offset;
    MatchPos() : group(-1), select(0), offset(0) {}
  };
  struct SpellStr {
    const wchar_t* str;  // 保存在m_buffer中的指针
    int len;             // str的长度
    int pos;             // 对应原始文本m_text中的索引
  };
  typedef SFixedArray<SpellStr, SpellManager::MAX_SPELL_COUNT> tSpellUnit;
  typedef tSpellUnit tSpellUnitArray[pinyin::MAX_HANZI_LENGTH];
  tSpellUnitArray m_spells;  // tSpellUnit数组，最大支持128个
  int m_count;               // tSpellUnit个数

  const wchar_t* m_text;          // 当前要检查的文本
  int m_text_len;                 // 当前要检查的文本的长度
  int m_parse_pos;                // 当前已经解析到的位置
  SpellManager* m_spell_manager;  // 拼音管理器
  SStringBuf m_buffer;  // 供tSpellUnit使用的内部内存分配器，SetText调用后重置
  bool m_full_spell;            // 当前是否是全拼模式
  SimpleArray<wchar_t> m_temp;  // 连续非多音字的拼音构成的临时内存
  int m_temp_pos;
  MatchPos m_match_pos;     // 匹配的准确位置
  vector<int> m_multi_pos;  // 多音字的位置
  bool m_flag_debug;

 public:
  SpellMatch(SpellManager* spellmgr)
      : m_spell_manager(spellmgr),
        m_flag_debug(false),
        m_temp(1024),
        m_temp_pos(-1),
        m_full_spell(true),
        m_parse_pos(0),
        m_text_len(0),
        m_text(0),
        m_count(0) {}
  bool IsMatchKey(const wchar_t* text, int tlen, bool full, const wchar_t* key,
                  int len) {
    if (text == nullptr || tlen <= 0 || len <= 0 || key == nullptr)
      return false;
    SetText(text, tlen, full);
    for (int group = 0;; group++) {
      if (!ParseUntilPolySpell(group)) return false;
      if (IsMatchKeyFrom(key, len, group, -1)) return true;
    }
    return false;
  }
  bool IsAlignedMatch(const wchar_t* text, int tlen, const wchar_t* key,
                      int len) {
    class AlignMatcher {
      enum { MAX_CACHE_LENGTH = 256 };
      typedef SpellManager::tSpellIds tSpellIds;
      typedef tSpellIds SpellIdArray[MAX_CACHE_LENGTH];
      typedef bool BoolArray[MAX_CACHE_LENGTH];
      SpellIdArray m_spells;
      BoolArray m_gates;
      tSpellIds m_lastid;
      SpellManager* m_manager;
      const wchar_t* m_text;
      int m_length;

     public:
      AlignMatcher(SpellManager* manager, const wchar_t* text, int tlen)
          : m_manager(manager), m_text(text), m_length(tlen) {
        memset(m_gates, false, MAX_CACHE_LENGTH * sizeof(bool));
      }
      const tSpellIds& GetSpells(int pos, bool& valid) {
        if (pos <= MAX_CACHE_LENGTH && m_gates[pos]) {
          valid = m_gates[pos];
          return m_spells[pos];
        }
        m_lastid.clear();
        wchar_t prev = pos >= 1 ? m_text[pos - 1] : 0;
        wchar_t next = (pos + 1 < m_length) ? m_text[pos + 1] : 0;
        valid =
            m_manager->GetSpellIdEx(m_text[pos], m_lastid, true, prev, next);
        if (valid && pos <= MAX_CACHE_LENGTH) {
          m_gates[pos] = valid;
          m_spells[pos] = std::move(m_lastid);
        }
        return m_lastid;
      }
      bool IsAlignedMatchFrom(int pos, const wchar_t* key, int len) {
        int i = pos;
        bool valid = false;
        const tSpellIds& spell = GetSpells(i, valid);
        if (valid) {
          tSpellUnit sunit;
          for (int k = 0; k < spell.size(); k++) {
            const SpellManager::SpellInfo& si =
                m_manager->GetSpellInfo(spell.at(k));
            int namelen = si.len < len ? si.len : len;
            if (wcsncmp(si.name.data, key, namelen) == 0) {
              if (len == namelen ||
                  IsAlignedMatchFrom(i + 1, key + namelen, len - namelen))
                return true;
            }
          }
          return false;
        } else {
          if (*key == m_text[i] &&
              (len == 1 || IsAlignedMatchFrom(i + 1, key + 1, len - 1)))
            return true;
          return false;
        }
        return false;
      }
      bool IsAlignedMatchWith(const wchar_t* key, int len) {
        for (int i = 0; i < m_length; i++) {
          if (IsAlignedMatchFrom(i, key, len)) return true;
        }
        return false;
      }
    };
    AlignMatcher mather(m_spell_manager, text, tlen);
    return mather.IsAlignedMatchWith(key, len);
  }
  int GetLastMatchPos() const {
    /*
    m_match_pos
    group 表示m_spells的索引
    select表示m_slells[group]的索引
    offset表示SpellStr.str的偏移量
    */
    if (m_match_pos.group >= 0 && m_match_pos.group < m_count) {
      const tSpellUnit& su = m_spells[m_match_pos.group];
      if (m_match_pos.select >= 0 && m_match_pos.select < su.size()) {
        const SpellStr& ss = su.at(m_match_pos.select);
        return ss.pos;
      }
    }
    return -1;
  }
  bool GetSpell(const wchar_t* text, int tlen, wchar_t* fullspell, int& flen,
                wchar_t* shortspell, int& slen) {
    if (text == 0 || tlen <= 0) {
      flen = slen = 0;
      return false;
    }
    bool bfull = fullspell && flen > 0;
    bool bshort = shortspell && slen > 0;
    int fullsuc = 0, shortsuc = 0;
    if (bfull) {
      fullsuc = GetSpellImpl(text, tlen, fullspell, flen, true);
    } else
      flen = 0;
    if (bshort) {
      shortsuc = GetSpellImpl(text, tlen, shortspell, slen, false);
    } else
      slen = 0;
    return fullsuc > 0 && shortsuc > 0;
  }

 protected:
  bool GetTwoSpell(const wchar_t* text, int tlen, wchar_t* fullspell, int& flen,
                   wchar_t* shortspell, int& slen) {
    // 全拼简拼一起添加,性能比各自单独添加要快,因为SetText和ParseUntilPolySpell不用重复做
    // 此段代码有问题,在于ParseUntilPolySpell永远返回的都是全拼
    BASSERTS(fullspell && flen > 0 && shortspell && slen > 0, "");
    SetText(text, tlen, true);
    for (int group = 0;; group++) {
      if (!ParseUntilPolySpell(group)) break;
    }

    /*
    m_spells将text划分为若干个单元,前后衔接的不是多音字的汉字合并成一个单元,
    有多音字的汉字单个占据一个单元

    TraverseCountArray用于遍历所有可能的组合:从多音字单元中抽选一个
    通过GetNextState来遍历,直至结束
    */
    TraverseCountArray<pinyin::MAX_HANZI_LENGTH> traverse;
    for (int i = 0; i < m_count; i++) {
      traverse.AddCountInfo(m_spells[i].size());
    }
    traverse.StartTraverse();
    int fpos = 0, spos = 0;
    int state_count = 0;
    do {
      if (fpos > 0 && fpos + 1 < flen - 1) fullspell[fpos++] = ',';
      if (spos > 0 && spos + 1 < slen - 1) shortspell[spos++] = ',';

      // 将当前编译的一个状态对应的拼音拷贝出来
      for (int i = 0; i < m_count; i++) {
        const tSpellUnit& su = m_spells[i];
        int index = traverse.GetPos(i);
        const SpellStr& ss = su.at(index);
        if (fpos + ss.len < flen - 1) {
          wcsncpy(fullspell + fpos, ss.str, ss.len);
          fpos += ss.len;
        }
        if (spos + 1 < slen - 1) shortspell[spos++] = *ss.str;
      }
      state_count++;
      if (fpos + 1 >= flen && slen + 1 >= slen) break;
    } while (traverse.GetNextState());

    fullspell[fpos] = 0;
    shortspell[spos] = 0;
    flen = fpos;
    slen = spos;
    return true;
  }

  int GetSpellImpl(const wchar_t* text, int tlen, wchar_t* fullspell, int& flen,
                   bool full) {
    SetText(text, tlen, full);
    for (int group = 0;; group++) {
      if (!ParseUntilPolySpell(group)) break;
    }

    /*
            m_spells将text划分为若干个单元,前后衔接的不是多音字的汉字合并成一个单元,
            有多音字的汉字单个占据一个单元

            TraverseCountArray用于遍历所有可能的组合:从多音字单元中抽选一个
            通过GetNextState来遍历,直至结束
    */
    TraverseCountArray<pinyin::MAX_HANZI_LENGTH> traverse;
    for (int i = 0; i < m_count; i++) {
      traverse.AddCountInfo(m_spells[i].size());
    }
    traverse.StartTraverse();
    int fpos = 0;
    int state_count = 0;
    do {
      if (fpos > 0 && fpos + 1 + 1 < flen) {
        fullspell[fpos++] = ',';
      }
      for (int i = 0; i < m_count; i++) {
        const tSpellUnit& su = m_spells[i];
        int index = traverse.GetPos(i);
        const SpellStr& ss = su.at(index);
        if (fpos + ss.len < flen - 1) {
          wcsncpy(fullspell + fpos, ss.str, ss.len);
          fpos += ss.len;
        }
      }
      state_count++;
      if (fpos + 1 >= flen) break;
    } while (traverse.GetNextState());

    if (fullspell && fpos < flen) fullspell[fpos] = 0;
    flen = fpos;
    return state_count;
  }
  void SetText(const wchar_t* text, int len, bool full) {
    m_full_spell = full;
    m_text = text;
    m_text_len = len;
    m_buffer.Reset();
    m_parse_pos = 0;
    m_count = 0;
    m_match_pos = MatchPos();

    // 根据分析,只需要将m_count设成0,那么m_spells无需清空
    // memset(m_spells, 0, sizeof(tSpellUnit)*pinyin::MAX_HANZI_LENGTH);
    m_temp.clear();
    m_temp_pos = -1;
    if (m_flag_debug) m_multi_pos.clear();
  }
  bool ParseUntilPolySpell(int index) {
    // 将连续的非多音字的文本组合到一个tSpellUnit中
    // 将多音字的所有文本组合到一个tSpellUnit中
    // 此函数可改造成需要时调用，使得IsMatchKey能够提前返回
    // 此函数一直解析，直至index<m_count或者末尾
    // m_count是当前多音字分组的个数
    if (index < m_count) {
      // 通常index都会比m_count大,表示当前所期望的下一个分组的索引
      // m_count是当前分组的个数
      return true;
    }
    m_temp.clear();
    m_temp_pos = -1;
    while (m_parse_pos < m_text_len) {
      int ppos = m_parse_pos++;
      SpellManager::tSpellIds ids;
      wchar_t prev = ppos >= 1 ? m_text[ppos - 1] : 0,
              next = (ppos + 1 < m_text_len) ? m_text[ppos + 1] : 0;
      if (m_spell_manager->GetSpellIdEx(m_text[ppos], ids, m_full_spell, prev,
                                        next)) {
        if (ids.size() > 1)  // 多音字
        {
          if (m_flag_debug) m_multi_pos.push_back(ppos);
          tSpellUnit sunit;
          for (int i = 0; i < ids.size(); i++) {
            const SpellManager::SpellInfo& si =
                m_spell_manager->GetSpellInfo(ids.at(i));
            SpellStr ss;
            ss.pos = ppos;
            ss.str =
                m_buffer.AddString(si.name.data, m_full_spell ? si.len : 1);
            ss.len = m_full_spell ? si.len : 1;
            sunit.Add(ss);
          }
          AddLastNonPolySpell();
          AddSpellUnit(std::move(sunit));
          if (index < m_count) return true;
        } else if (ids.size() == 1) {
          const SpellManager::SpellInfo& si =
              m_spell_manager->GetSpellInfo(ids.at(0));
          m_temp.append(si.name.data, m_full_spell ? si.len : 1);
          m_temp_pos = (-1 == m_temp_pos) ? ppos : m_temp_pos;
        }
      } else {
        // 将如下字符添加到
        m_temp.Add(m_text[ppos]);
        m_temp_pos = (-1 == m_temp_pos) ? ppos : m_temp_pos;
      }
    }
    AddLastNonPolySpell();
    return index < m_count;
  }
  void AddLastNonPolySpell() {
    if (m_temp.size() > 0) {
      tSpellUnit lastunit;
      SpellStr ss;
      ss.pos = m_temp_pos;
      ss.str = m_buffer.AddString(m_temp.GetBuffer(), m_temp.size());
      ss.len = m_temp.size();
      lastunit.Add(ss);
      m_temp.clear();
      m_temp_pos = -1;
      AddSpellUnit(std::move(lastunit));
    }
  }
  void AddSpellUnit(tSpellUnit&& sunit) {
    m_spells[m_count] = std::move(sunit);
    m_count++;
  }

  bool IsMatchKeyFrom(const wchar_t* key, int len, int i, int offset) {
    // 递归调用
    // 从第i个分组开始,检测指定key与该分组之后的所有可能分支是否匹配
    // offset只有-1和0两个值,-1表示尝试该分组的每个拼音串的对齐定位
    //       0表示整个拼音串的匹配
    // 通常,-1用在最开始
    BASSERTS(i >= 0 && i < m_count, "");
    if (i >= m_count) {
      ParseUntilPolySpell(i);
    }
    const tSpellUnit& spell = m_spells[i];
    for (int k = 0; k < spell.size(); k++) {
      const SpellStr& ss = spell[k];
      if (offset < 0) {
        // 将key与该汉字的每个多音拼音的序列尝试对齐
        wchar_t code = *key;
        for (int j = 0; j < ss.len; j++) {
          if (ss.str[j] != code) continue;

          // 对齐后,
          SpellStr sp;
          sp.str = ss.str + j;
          sp.len = ss.len - j;
          if (IsMatchFromSpell(sp, key, len, i)) {
            m_match_pos.group = i;
            m_match_pos.select = k;
            m_match_pos.offset = j;
            return true;
          }
        }
      } else {
        if (IsMatchFromSpell(ss, key, len, i)) {
          m_match_pos.group = i;
          m_match_pos.select = k;
          m_match_pos.offset = 0;
          return true;
        }
      }
    }
    return false;
  };
  bool IsMatchFromSpell(const SpellStr& ss, const wchar_t* key, int len,
                        int i) {
    // 当前是第i个分组
    // 此分组中有若干个拼音串
    // ss只是其中之一,且其已经做了开头截断处理
    // 此函数判断ss的拼音串是否与key开头的字符串相等
    // 如果相等,那么key会变短,如果还有,继续下一个分组的匹配,如果没有,那么表示匹配成功
    if (ss.len <= len) {
      if (wcsncmp(ss.str, key, ss.len) == 0) {
        if (len <= ss.len) return true;

        // 准备下一个分组
        if (!ParseUntilPolySpell(i + 1))  // 末尾了
        {
          BASSERTS(m_parse_pos >= m_text_len,
                   "");  // 断言给定的文本已经解析完毕
          return len > ss.len ? false : true;
        }

        // key会变短,继续测试下一个分组的匹配
        if (IsMatchKeyFrom(key + ss.len, len - ss.len, i + 1, 0)) return true;
      }
    } else {
      if (wcsncmp(ss.str, key, len) == 0) return true;
    }
    return false;
  }
};

//--------------------------------------------------------------------------
// 拼音管理器
//--------------------------------------------------------------------------
SpellManager::SpellManager()
    : m_spellid_map(NULL),
      m_link_char(','),
      m_ignore_tone(false),
      m_spell_capital(true),
      m_prefixmap(NULL),
      m_use_context(true) {
  m_spell_matcher = new SpellMatch(this);
  m_spells = new SimpleArray<SpellInfo>();
  m_spellid_map = new HanziSpellMap(GetHashPrimeLargerThan(20000));
}

SpellManager::~SpellManager() {
  if (m_spell_matcher) delete m_spell_matcher;
  if (m_prefixmap) delete m_prefixmap;
  if (m_spellid_map) delete m_spellid_map;
  if (m_spells) delete m_spells;
}
void SpellManager::LoadDefaultDict() {
  // 直接加载已经处理好的二进制字典序列
  {
    BS_METRICS("[PINYIN][METRIC]LoadDict(gs_pinyin_array)");
    LoadDict(gs_pinyin_array, _countof(gs_pinyin_array));
  }

  { AddDefaultPrefixLines(); }

  BASSERTS(m_prefixmap, "m_prefixmap不能为空,请检查代码编码问题");

  int mem = 0;
  int itemsize =
      sizeof(tHanzi) + sizeof(tSpellIds) + sizeof(unsigned int) + sizeof(void*);
  mem += itemsize * (m_spellid_map ? m_spellid_map->Count() : 0);
  mem += m_spells->size() * sizeof(SpellInfo);
  if (m_spellid_map && m_prefixmap) {
    mem += m_prefixmap->Count() * (sizeof(SpellContext) + sizeof(wchar_t) +
                                   sizeof(unsigned int) + sizeof(void*));
  }
  mem += sizeof(m_all_builder) + sizeof(m_short_builder);
  BLOGDEBUG("[MEMORY]SpellManager consume %d bytes", mem);
}
void SpellManager::LoadDict(const wchar_t* dictArray, int size) {
  // 每行是如下格式
  // len,code,pinyin,0,pinyin,0,...
  // 每行第一个是长度
  //     第二个是汉字
  //     之后是以0结尾的拼音,如上
  // 此文件通过首先LoadDict调用原始文件,然后ExportDict导出来生成
  unsigned int len = 0;
  tHanzi hz = {0};
  SpellInfo si = {0};
  wchar_t a = 'a', z = 'z', offset = 'a' - 'A';
  for (int i = 0; i < size; i += len) {
    const wchar_t* base = dictArray + i;
    len = (unsigned int)base[0];
    hz.code = base[1];
    tSpellIds spellids;
    if (m_spellid_map->Find(hz, spellids)) continue;
    const wchar_t* start = dictArray + i + 2;
    const wchar_t* end = dictArray + i + len;
    while (start < end) {
      memset(&si, 0, sizeof(SpellInfo));
      si.tone = 0;
      si.len = (char)wcslen(start);
      wcsncpy(si.name.data, start, MAX_SPELL_LEN - 1);  // TODO OK?
      if (m_spell_capital) {
        for (int i = 0; i < MAX_SPELL_LEN; i++) {
          wchar_t c = si.name.data[i];
          if (c == 0) break;
          si.name.data[i] = (c >= a && c <= z) ? (c - offset) : c;
        }
      }
      m_spells->push_back(si);
      spellids.Add(m_spells->size() - 1);
      start += si.len + 1;
    }
    m_spellid_map->Insert(hz, spellids);
  }
}
bool SpellManager::ExportDict(const BaseString& fullDictFile) {
  class CodBuffer {
    char* m_buffer;
    int m_capcity;
    int m_size;
    int m_length;  // 宽字符的个数;
    bool m_newline;
#ifdef WIN32
    void* m_file;
#else
    int m_file;
#endif
   public:
    CodBuffer(int capcity)
        : m_size(0), m_capcity(capcity), m_length(0), m_newline(false) {
#ifdef WIN32
      m_file = 0;
#else
      m_file = -1;
#endif
      m_buffer = new char[m_capcity];
      memset(m_buffer, 0, m_capcity);
    }
    ~CodBuffer() {
      Flush();
      if (m_buffer) delete[] m_buffer;
#ifdef WIN32
      if (m_file) {
        FlushFileBuffers(m_file);
      }
      if (m_file) CloseHandle(m_file);
#else
      if (m_file != -1) {
        close(m_file);
        m_file = -1;
      }
#endif
    }
    bool Init(const char* fullFileName) {
#ifdef WIN32
      m_file = CreateFileA(fullFileName, GENERIC_READ | GENERIC_WRITE,
                           FILE_SHARE_READ, NULL, CREATE_ALWAYS,
                           FILE_ATTRIBUTE_NORMAL, NULL);
      if (m_file == INVALID_HANDLE_VALUE || m_file == 0) {
        m_file = NULL;
        return false;
      }
      SetFilePointer(m_file, 0, 0, FILE_BEGIN);
#else
      m_file = open(fullFileName, O_RDWR | O_CREAT, 0777);
      if (m_file == -1) {
        return false;
      }

      lseek(m_file, 0, SEEK_SET);
#endif
      return true;
    }
    void AddLineEnd() { m_newline = true; }
    void AddContent(wchar_t c) { AddContent(&c, 1); }
    void AddContent(const wchar_t* str, int len) {
      // wchar_t c1[] = { 12,0x6211,' ','w', 'o',0};
      // 将str解码成hex,要c++能直接引入
      // '\x09','\xe3'
      for (int i = 0; i < len; i++) {
        char buf[12] = {0};
        int size = StringUtil::tohex(str[i], buf);
        if (m_length > 0) {
          AddString(",", 1);
          if (m_newline) {
            AddString("\n", 1);
            m_newline = false;
          }
        }
        AddString(buf, size);
        m_length++;
      }
    }

   protected:
    void AddString(const char* str, int len) {
      if (str && len > 0) {
        if (len + m_size > m_capcity) Flush();
        BASSERT(len + m_size < m_capcity);
        memcpy(m_buffer + m_size, str, len);
        m_size += len;
      }
    }
    void Flush() {
#ifdef WIN32
      DWORD write = 0;
      if (m_buffer && m_size > 0 && m_file) {
        WriteFile(m_file, m_buffer, m_size, &write, 0);
      }
#else
      if (m_buffer && m_size > 0 && m_file != -1) {
        write(m_file, m_buffer, m_size);
      }
#endif
      if (m_buffer && m_capcity > 0) memset(m_buffer, 0, m_capcity);
      m_size = 0;
    }
  };
  CodBuffer file(64 * 1024);
  if (!file.Init(fullDictFile.c_str())) return false;
  wchar_t blank = ' ';
  std::vector<wchar_t> codes;
  codes.reserve(m_spellid_map->Count());
  HanziSpellMap::iterator it = m_spellid_map->begin();
  for (; it != m_spellid_map->end(); ++it) {
    codes.push_back(it.first().code);
  }
  std::sort(codes.begin(), codes.end());
  tHanzi hz;
  for (int index = 0; index < (int)codes.size(); index++) {
    hz.code = codes[index];
    tSpellIds id;
    if (!m_spellid_map->Find(hz, id)) continue;
    int hanlen = 2;  // 长度,汉字,共2个
    for (int i = 0; i < id.size(); i++) {
      const SpellInfo& si = GetSpellInfo(id[i]);
      hanlen += si.len + 1;  // 拼音,结束0字符
    }
    file.AddContent((wchar_t)hanlen);
    file.AddContent(&hz.code, 1);
    for (int i = 0; i < id.size(); i++) {
      const SpellInfo& si = GetSpellInfo(id[i]);
      file.AddContent(si.name.data, si.len);
      file.AddContent(0);
    }
    file.AddLineEnd();
  }
  return true;
}
void SpellManager::LoadDict(const BaseString& fullDictFile) {
  // utf8_to_hanyu_pinyin.txt
  // 9FA5 (yue4)
  // 原版多音字,汉字以hex编码显示
  // BaseString fname =
  // "E:\\Sumscope\\QB\\dev\\quoteboard\\projects\\Pinyin\\unicode_to_hanyu_pinyin.txt";

  // 删除了不常用的多音字,utf8编码
  BaseString key = " \t(),";
  // ScopedFileHandle file = ;
  std::vector<BaseString> lines;
  StringUtil::LoadLinesFromFile(G2W(fullDictFile), lines);
  m_spells->push_back(SpellInfo());
  m_spells->setcapcity(lines.size());
  for (int i = 0; i < (int)lines.size(); i++) {
    BaseString& line = lines[i];
    AddSpellLine(line.c_str(), line.size(), i, true);
  }
  AddDefaultPrefixLines();
}
void SpellManager::AddSpellLine(const char* linestr, int len, int i,
                                bool isutf8) {
  // 文本行：6332 (suo1,sa1,sha1)
  // 第一个是utf8编码
  strinfo tokens[256];
  BaseString key = " \t(),";
  int tknum = StringUtil::SplitStringInfo(linestr, len, key, tokens, 256);
  if (tknum < 2) {
    //    qb::base::DebugView(0, 0, 0, "invalid line:%d %s", i, linestr);
    return;
  }
  tHanzi hz = {0};
  if (isutf8)
    hz.code = *m_convert.UToWide(linestr + tokens[0].pos, tokens[0].len);
  else
    hz.code = HexStrToWChar(linestr + tokens[0].pos);
  tSpellIds spellids;
  if (m_spellid_map->Find(hz, spellids)) {
    BVIEW("line already exist:%d %s", i, linestr);
    return;
  }
  spellids.clear();
  SimpleArray<SpellInfo> spells;  // 非重复
  for (int k = 1; k < tknum; k++) {
    const char* str = linestr;
    SpellInfo si = {0};
    strinfo& token = tokens[k];
    si.tone = str[token.pos + token.len - 1];
    si.len = token.len - 1;
    if (si.tone < '0' || si.tone > '9') {
      si.tone = '0';
      si.len++;
    }
    const wchar_t* wstr = m_convert.ToWide(str + token.pos, si.len);
    wcsncpy(si.name.data, wstr, _TRUNCATE);
    // memcpy(si.name.data, str + token.pos, si.len);
    if (si.len <= 0) {
      BVIEW("invalid token:%d %d %s", i, k,
            BaseString(str + token.pos, token.len).c_str());
      continue;
    }
    if (m_ignore_tone) {
      bool skiped = false;
      for (int j = 0; j < spells.size(); j++) {
        SpellInfo& oldsi = spells[j];
        if (oldsi.len == si.len &&
            wcsncmp(oldsi.name.data, si.name.data, si.len) == 0)
          skiped = true;
      }
      if (skiped) continue;
    }
    spells.push_back(si);
  }
  for (int j = 0; j < spells.size(); j++) {
    m_spells->push_back(spells[j]);
    spellids.Add(m_spells->size() - 1);
  }
  m_spellid_map->Insert(hz, spellids);
}
void SpellManager::AddDefaultPrefixLines() {
  /*
          以|间隔,将每行文本划分为多个区域
          第一个是指定的汉字,之后每个区域以逗号,分割,第一个是其拼音,第二个表示前缀还是后缀,后面的是与该拼音搭配的汉字
          这些多音字定义,会更新m_prefixmap对象
          只有当m_use_context为true的时候,这些信息修正才会起作用
          默认是true,默认就起作用,可以通过SetUseContext接口来开启或者关闭
          以下是经过统计发现消耗资源最多的多音字的修正信息
  */
  AddPrefixLine(
      L"行|hang,prefix,银,商,分,招,支,农,中,建,央,合,工,交,闵,总,亚|xing,"
      L"prefix,旅,之,发|hang,post,情|xing,post,政");
  AddPrefixLine(
      L"大|da,prefix,光,北,恒,正,浙,宏,拿|da,post,连,唐,同,桥,众,理,国,冶,都,"
      L"江,地,庆,通,丰");
  AddPrefixLine(
      L"长|zhang,prefix,省,司,市,州,部|chang,prefix,延|chang,post,春,沙,安,江,"
      L"城,兴,虹,寿,乐,青,治,影,电,运,峰,岛,信,航,建,交,投,轨");
  AddPrefixLine(L"重|zhong,post,工,汽,钢,型,机|chong,post,庆");
  AddPrefixLine(L"波|bo,prefix,宁");
  AddPrefixLine(L"厦|xia,post,门,国|sha,prefix,商,大,广");
  AddPrefixLine(
      L"地|di,prefix,房,绿,天,汉,土,基,置,大|di,post,产,铁,方,区,理,下,质,热");
  AddPrefixLine(L"都|du,prefix,成,首,江,大,尧,药,国|du,post,市,城,江");
  AddPrefixLine(
      L"省|sheng,post,分,政,长|sheng,prefix,南,东,西,江,苏,北,建,川,肃,州,林,"
      L"海,宁,徽,大");
  AddPrefixLine(L"莞|guan,prefix,东");
  AddPrefixLine(
      L"车|che,prefix,汽,北,南,中,停,轮,客,宇,马,库,汉,遵,租|che,post,灯,业,库,"
      L"都,王");
  AddPrefixLine(L"率|lv,prefix,几,利,功,频");
  AddPrefixLine(L"术|shu,prefix,技");
  AddPrefixLine(L"度|du,prefix,年");
  AddPrefixLine(L"盛|sheng,prefix,高,华,恒,丰,荣,国,新,龙|sheng,post,京,投,世");
  AddPrefixLine(
      L"石|shi,prefix,中,巨,黄,宝,山,矿,理|shi,post,狮,油,化,城,桥,河,嘴");
  AddPrefixLine(L"齐|qi,prefix,齐,天,木|qi,post,鲁,哈,峰,商,河,成");
  AddPrefixLine(L"乐|le,post,山,视,园,普,平,陶,清|le,prefix,昌,娱,凯,长");
  AddPrefixLine(L"枝|zhi,prefix,攀枝|zhi,post,江,花");
  AddPrefixLine(L"期|qi,post,货,债|qi,prefix,星,年,续,安,泰,分,中,长,短,久");
  AddPrefixLine(L"余|yu,prefix,新|yu,post,姚,杭,城,创,经,燃,交");
  AddPrefixLine(L"谷|gu,prefix,阳,光,金,山,农,件,盘,府,京,麓,红|gu,post,农,财");
  AddPrefixLine(L"阿|a,post,拉,克,里,勒,特,波|e,prefix,东|e,post,胶");
  AddPrefixLine(L"传|chuan,post,播,媒,化");
  AddPrefixLine(
      L"宿|su,post,迁,愿,志,疾,敌,心,雨,根,草,舍|su,prefix,住,留,露,营,名|xiu,"
      L"prefix,一,两,星");
  AddPrefixLine(L"虹|hong,prefix,彩,华,天,雨,长|hong,post,桥,阳");
  AddPrefixLine(L"射|she,post,阳,箭,击,手,程,洪");
  AddPrefixLine(
      L"强|qiang,prefix,华,五,永,强|qiang,post,生,债|jiang,prefix,倔");
  AddPrefixLine(
      L"奇|qi,post,瑞,闻,迹,观,计,怪|qi,prefix,拓,浪,正|ji,post,树,零,羡");
  AddPrefixLine(
      L"澄|cheng,post,湖,星,港,净,碧,澈|cheng,prefix,阳|deng,post,清,沙,泥");
  AddPrefixLine(L"食|shi,post,品,业|shi,prefix,粮,饮");
  AddPrefixLine(L"会|hui,prefix,员,社,新,都,新|kuai,post,计");
  AddPrefixLine(L"属|shu,prefix,金,直,下");
  AddPrefixLine(L"朝|chao,post,阳,国,资,建|zhao,post,晖,日");
  AddPrefixLine(
      L"屯|tun,prefix,家,盛,大,粮,姑|tun,post,区,河,粮,扎,守,田|zhun,post,蹇");
  AddPrefixLine(
      L"藏|zang,prefix,西,宝,三,道|zang,post,府,经,族|cang,prefix,埋,包,隐,收,"
      L"储|cang,post,奸,匿,品,书");
  AddPrefixLine(L"氏|shi,prefix,温,宏,族|zhi,prefix,阏,月");
  AddPrefixLine(L"句|ju,post,容,福,农|gou,prefix,高");
  AddPrefixLine(L"番|pan,post,禺|fan,post,帮,茄,薯|fan,prefix,鲁,吐");
  AddPrefixLine(L"革|ge,prefix,皮");
  AddPrefixLine(L"巷|xiang,prefix,房|xiang,post,口");
  AddPrefixLine(L"种|zhong,prefix,特,品|zhong,post,业");
  AddPrefixLine(L"圈|quan,prefix,西,游");
  AddPrefixLine(L"乾|qian,prefix,开,兴");
  AddPrefixLine(L"厂|chang,prefix,船,机,铝,电,大");
  AddPrefixLine(L"漯|luo,post,河,城");
  AddPrefixLine(L"卡|ka,post,森,特");
  AddPrefixLine(L"熟|shu,prefix,常");
  AddPrefixLine(L"地|di,post,债,方,名,铁");
  AddPrefixLine(L"呗|bei,prefix,花,借");
  AddPrefixLine(L"盟|meng,prefix,太");
  AddPrefixLine(L"喀|ka,post,什");
  AddPrefixLine(L"什|shi,prefix,喀");
  AddPrefixLine(L"和|he,prefix,共,呼,融,中,六,上,颐,信,昌|he,post,国,浩,平");
  AddPrefixLine(L"佛|fo,post,山");
  AddPrefixLine(L"蚌|beng,post,埠");
  AddPrefixLine(L"络|luo,prefix,网");
  AddPrefixLine(L"宅|zhai,prefix,住,公|zhai,post,建,基");
  AddPrefixLine(L"色|se,prefix,有,绿,蓝,红,特,春,金,百");
}
void SpellManager::AddPrefixLine(const wchar_t* line, int len) {
  if (m_spell_capital) {
    wchar_t offset = 'a' - 'A';
    len = len <= 0 ? wcslen(line) : len;
    wstring wstr;
    wstr.reserve(len);
    for (int i = 0; i < len; i++) {
      if (line[i] >= 'a' && line[i] <= 'z')
        wstr.push_back(line[i] - offset);
      else
        wstr.push_back(line[i]);
    }
    AddPrefixLineImpl(wstr.c_str(), wstr.size());

  } else
    AddPrefixLineImpl(line, len);
}
void SpellManager::AddPrefixLineImpl(const wchar_t* line, int len) {
  // 行|hang,prefix,宁,...|hang,post,,,,|
  if (line && len <= 0) len = wcslen(line);
  if (line == 0 || len <= 0) return;
  strinfo tokens[256];
  strinfo words[256];
  int tk = StringUtil::SplitStringInfo(line, len, L"|", tokens, 256);
  if (tk < 2) return;

  wchar_t code = *(line + tokens[0].pos);
  tSpellIds id;
  GetSpellId(code, id);
  SpellContext* context = 0;
  for (int i = 1; i < tk; i++) {
    const wchar_t* prefix = line + tokens[i].pos;
    int prefixlen = tokens[i].len;
    int nw = StringUtil::SplitStringInfo(prefix, prefixlen, L",", words, 256);
    if (nw < 3) continue;
#ifdef WIN32
    if (_wcsnicmp(prefix + words[1].pos, L"prefix", words[1].len) == 0)
#else
    if (wcsncasecmp(prefix + words[1].pos, L"prefix", words[1].len) == 0)
#endif
    {
      int index = GetSpellIdByName(id, prefix + words[0].pos, words[0].len);
      if (index >= 0) {
        if (context == 0) context = new SpellContext(code);
        for (int k = 2; k < nw; k++) {
          context->AddPrefix(*(prefix + words[k].pos), index);
        }
      }
    }
#ifdef WIN32
    else if (_wcsnicmp(prefix + words[1].pos, L"post", words[1].len) == 0)
#else
    else if (wcsncasecmp(prefix + words[1].pos, L"post", words[1].len) == 0)
#endif
    {
      int index = GetSpellIdByName(id, prefix + words[0].pos, words[0].len);
      if (index >= 0) {
        if (context == 0) context = new SpellContext(code);
        for (int k = 2; k < nw; k++) {
          context->AddPost(*(prefix + words[k].pos), index);
        }
      }
    }
  }
  if (context) {
    if (!m_prefixmap)
      m_prefixmap = new SpellContextMap(GetHashPrimeLargerThan(79));
    m_prefixmap->Insert(code, (unsigned int)code, context);
  }
}
void SpellManager::GetPinyin(const wchar_t* buffer, wstring& sJianpin,
                             wstring& sQuanpin) {
  GetAllSpellForString(buffer, sQuanpin, sJianpin);
}
bool SpellManager::GetSpell(const wchar_t* text, int tlen, wchar_t* fullspell,
                            int& flen, wchar_t* shortspell, int& slen) {
  /*
          性能测试表明:
          此函数的性能是QB中的PinyinManager的两倍,且生成的拼音字符串更短
          使用此函数,可在220ms扫描6w条债券的code和name
          原先的QB中的PinyinManager扫描同样的数据需要400ms
  */
  return m_spell_matcher->GetSpell(text, tlen, fullspell, flen, shortspell,
                                   slen);
}

void SpellManager::GetPolySpell(const wstring& str, wstring& polyCharacters) {
  for (int i = 0; i < (int)str.size(); i++) {
    tSpellIds ids;
    if (GetSpellId(str[i], ids) && ids.size() > 1) {
      polyCharacters.push_back(str[i]);
    }
  }
}
#define UNIT_TEST
#ifdef UNIT_TEST
struct StatInfo {
  int count;
  int bytes;
  int longest;
};
class SpellStat : public SimpleArray<StatInfo> {
 public:
  std::vector<BaseString> m_max_names;

 public:
  void AddMaxNames(const BaseString& name) { m_max_names.push_back(name); }
};
static SpellStat _gs_spell_stat_;
#endif
void SpellManager::Dump() {
#ifdef UNIT_TEST
  PinyinStat::Get(this)->Dump();
  // MAX_SPELL_LEN	= 14,			// 拼音字符串的最大长度
  //	MAX_HANZI_LEN	= 4,			//
  //	MAX_SPELL_COUNT = 8				// 多音字的拼音最大个数

  // 经过测试，如下多音字产生消耗最多资源
  // 5:行,4:家,4:哈,4:莞,3:作,3:转,3:齐,3:期,3:罗,3:华,2:中,2:长,
  // 2:余,2:有,2:兴,2:夏,2:溪,2:乌,2:万,2:通,2:台,2:塔,2:盛,2:上,2:区,
  // 2:宁,2:南,2:丽,2:乐,2:经,2:济,2:化,2:红,2:合,2:广,2:分,2:发,2:都,2:大,2:波,
  int max_spell = 0;
  for (int i = 0; i < m_spells->size(); i++) {
    SpellInfo& si = m_spells->at(i);
    max_spell = max_spell < si.len ? si.len : max_spell;
  }
  int max_spell_count = 0;
  int max_hanzi_len = 0;
  auto it = m_spellid_map->begin();
  for (; it != m_spellid_map->end(); ++it) {
    const tHanzi& hz = it.first();
    const tSpellIds& id = it.second();
    int hanlen = 0;
    for (int i = 0; i < id.size(); i++) {
      const SpellInfo& si = GetSpellInfo(id[i]);
      hanlen += si.len;
    }
    max_hanzi_len = max_hanzi_len < hanlen ? hanlen : max_hanzi_len;
    max_spell_count = max_spell_count < it.second().size() ? it.second().size()
                                                           : max_spell_count;
  }

  // wchar_t code = L'行';
  // tHanzi hz={0};
  // CConvertFast convert;
  // const char* gbkstr = convert.ToGbk(&code,1);
  // int datalen = convert.GetStrSize();
  // BASSERT(datalen>0 && datalen<MAX_HANZI_LEN);
  // memcpy(hz.data,gbkstr,datalen);
  // tSpellIds ids;
  // m_spellid_map->Find(hz,ids);

  vector<BaseString> lines;
  lines.push_back(
      os::fmtstr("%d,%d,%d", max_spell, max_spell_count, max_hanzi_len));
  for (int i = 0; i < _gs_spell_stat_.size(); i++) {
    StatInfo& si = _gs_spell_stat_.at(i);
    lines.push_back(os::fmtstr("%d,%d,%d", si.count, si.bytes, si.longest));
  }
  StringUtil::WriteLines(lines, "pinyin.stat.txt");
  StringUtil::WriteLines(_gs_spell_stat_.m_max_names, "pinyin.stat.names.txt");
#endif
}

////////////////////////////////////////////////////////////////////////////////
class ShortSpellMatch {
  typedef SFixedArray<wchar_t, SpellManager::MAX_SPELL_COUNT> tSpellFirst;
  typedef tSpellFirst tSpellFirstArray[pinyin::MAX_HANZI_LENGTH];
  tSpellFirstArray m_spells;
  int m_count;

 public:
  bool IsMatchKey(const wchar_t* key, int len) {
    if (m_count < len || len <= 0 || key == 0) return false;
    for (int i = 0; i < m_count; i++) {
      tSpellFirst& spell = m_spells[i];
      for (int j = 0; j < spell.size(); j++) {
        if (spell.at(j) == key[0]) {
          if (IsSpellContainCode(i + 1, key[1]) &&
              IsSpellMatchFromPos(key + 1, len - 1, i + 1))
            return true;
          break;
        }
      }
    }
    return false;
  }

 protected:
  inline bool IsSpellContainCode(int pos, wchar_t code) {
    if (pos >= 0 && pos < m_count && m_spells[pos].find(code) >= 0) return true;
    return false;
  }
  bool IsSpellMatchFromPos(const wchar_t* key, int len, int pos) {
    if (len <= 0) return true;
    for (int i = pos; i < m_count; i++) {
      tSpellFirst& spell = m_spells[i];
      for (int j = 0; j < spell.size(); j++) {
        if (spell.at(j) == key[0]) {
          if (IsSpellContainCode(i + 1, key[1]) &&
              IsSpellMatchFromPos(key + 1, len - 1, i + 1))
            return true;
          break;
        }
      }
    }
    return false;
  }
};
bool SpellManager::IsMatchPinyin(const wchar_t* text, int tlen,
                                 const wchar_t* key, int klen, bool full,
                                 bool checkcn) {
  if (text == 0 || key == 0) return false;
  tlen = tlen <= 0 ? wcslen(text) : tlen;
  if (tlen <= 0) return false;
  klen = klen <= 0 ? wcslen(key) : klen;
  if (klen <= 0) return false;

  klen = klen >= pinyin::MAX_KEY_LENGTH ? pinyin::MAX_KEY_LENGTH : klen;
  tlen = tlen >= pinyin::MAX_HANZI_LENGTH ? pinyin::MAX_HANZI_LENGTH : tlen;
  if (checkcn) {
    for (int i = 0; i < klen; i++) {
      tSpellIds id;
      if (GetSpellId(key[i], id)) return false;
    }
  }
  return m_spell_matcher->IsMatchKey(text, tlen, full, key, klen);
}
bool SpellManager::IsAlignMatchPinyin(const wchar_t* text, int tlen,
                                      const wchar_t* key, int klen) {
  if (text == 0 || key == 0) return false;
  tlen = tlen <= 0 ? wcslen(text) : tlen;
  if (tlen <= 0) return false;
  klen = klen <= 0 ? wcslen(key) : klen;
  if (klen <= 0) return false;

  klen = klen >= pinyin::MAX_KEY_LENGTH ? pinyin::MAX_KEY_LENGTH : klen;
  tlen = tlen >= pinyin::MAX_HANZI_LENGTH ? pinyin::MAX_HANZI_LENGTH : tlen;
  return m_spell_matcher->IsAlignedMatch(text, tlen, key, klen);
}

int SpellManager::GetLastMatchPos() const {
  return m_spell_matcher->GetLastMatchPos();
}
bool SpellManager::GetAllSpellForString(const wstring& szStr, wstring& spell,
                                        wstring& shortSpell) {
  m_all_builder.Reset();
  m_short_builder.Reset();
  int flen = m_all_builder.GetCapcity();
  int slen = m_short_builder.GetCapcity();
  if (m_spell_matcher->GetSpell(szStr.c_str(), szStr.size(),
                                m_all_builder.GetBuf(), flen,
                                m_short_builder.GetBuf(), slen)) {
    spell = m_all_builder.GetBuf();
    shortSpell = m_short_builder.GetBuf();
    return true;
  }
  return false;

  // 旧实现,性能不好
  pinyin::HanziInfo hz_array[pinyin::MAX_HANZI_LENGTH] = {0};
  int hz_count =
      SplitIntoHanziArray(szStr.c_str(), hz_array, pinyin::MAX_HANZI_LENGTH);
  if (hz_count <= 0) return false;
  TraverseCountArray<pinyin::MAX_HANZI_LENGTH> traverse;
  for (int i = 0; i < hz_count; i++) {
    traverse.AddCountInfo(hz_array[i].found ? hz_array[i].ids.size() : 0);
  }
  m_all_builder.Reset();
  m_short_builder.Reset();
  traverse.StartTraverse();

  int state_count = 0, longest = 0;
  while (traverse.GetNextState()) {
    int curlong = m_all_builder.GetLength();
    m_all_builder.AddStart(m_link_char);
    m_short_builder.AddStart(m_link_char);
    for (int i = 0; i < hz_count; i++) {
      if (hz_array[i].found && hz_array[i].ids.size() > 0) {
        int idindex = traverse.GetPos(i);
        BASSERT(idindex >= 0 && idindex < hz_array[i].ids.size());

        int spell_id = hz_array[i].ids[idindex];
        BASSERT(spell_id >= 0 && spell_id < m_spells->size());

        const SpellInfo& spell_info = m_spells->at(spell_id);
        BASSERT(spell_info.len > 0);

        m_all_builder.AddString(spell_info.name.data, spell_info.len);
        m_short_builder.AddString(spell_info.name.data, 1);
      } else {
        m_all_builder.AddString(hz_array[i].name.code);
        m_short_builder.AddString(hz_array[i].name.code);
      }
    }

    state_count++;
    curlong = m_all_builder.GetLength() - curlong;
    longest = longest < curlong ? curlong : longest;
  }
  spell = m_all_builder.GetBuf();
  shortSpell = m_short_builder.GetBuf();

  // #ifdef UNIT_TEST
  //			StatInfo st;st.count =
  // state_count;st.bytes=m_all_builder.GetLength();st.longest=longest;
  //			_gs_spell_stat_.push_back(st);
  //			if(state_count>50||st.bytes>=500)
  //			{
  //				BaseString spellstr;
  //				for (int i=0;i<hz_count;i++)
  //				{
  //					spellstr+=os::fmtstr("%s:%d,",hz_array[i].name.data,hz_array[i].ids.size());
  //				}
  //				_gs_spell_stat_.AddMaxNames(os::fmtstr("state=%d,bytes=%d,str=[%s],spell=[%s]",state_count,st.bytes,(const
  // char*)szStr,spellstr.c_str()));
  //			}
  // #endif
  return true;
}

int SpellManager::GetSpellIdByName(const tSpellIds& ids, const wchar_t* spell,
                                   int len) {
  for (int i = 0; i < ids.size(); i++) {
    const SpellInfo& si = GetSpellInfo(ids[i]);
    if (len == si.len && wcsncmp(si.name.data, spell, len) == 0) return ids[i];
  }
  return -1;
}

int SpellManager::SplitIntoHanziArray(const wchar_t* wstr,
                                      pinyin::HanziInfo* parray, int capcity) {
  int len = wstr ? wcslen(wstr) : 0;
  if (len <= 0) return 0;
  int index = 0;
  for (int i = 0; i < len && index < capcity; i++) {
    pinyin::HanziInfo hi = {0};
    hi.name.code = wstr[i];
    hi.found = m_spellid_map->Find(hi.name, hi.ids);
    // parray[index++]=hi;
    memcpy(parray + index, &hi, sizeof(pinyin::HanziInfo));
    index++;
  }
  return index;
}

bool SpellManager::GetSpellId(wchar_t code, tSpellIds& ids) {
  tHanzi hz;
  hz.code = code;
  return m_spellid_map->Find(hz, ids);
}
bool SpellManager::GetSpellIdEx(wchar_t code, tSpellIds& ids, bool full,
                                wchar_t prev, wchar_t next) {
  tSpellIds tempid;
  tHanzi hz;
  hz.code = code;
  if (!m_spellid_map->Find(hz, tempid)) return false;

  // 根据前后字符来获取该汉字的拼音,只有当是多音字才有效果
  SpellContext* context = 0;
  if (m_use_context && tempid.size() > 1 && (prev != 0 || next != 0) &&
      m_prefixmap && m_prefixmap->Find(code, (unsigned int)code, context) &&
      context) {
    int index = -1;
    if (prev != 0 && context->GetSpellByPrefix(prev, index) && index >= 0) {
      ids.Add(index);
      return true;
    }
    index = -1;
    if (next != 0 && context->GetSpellByPost(next, index) && index >= 0) {
      ids.Add(index);
      return true;
    }
  }
  if (full) {
    ids = std::move(tempid);
    return true;
  }

  // 一个汉字的多个拼音,可能第一个字母是相同的,这里排除相同首字母的
  ids.Add(tempid[0]);
  wchar_t last_head = m_spells->at(tempid[0]).name.data[0];
  for (int i = 1; i < tempid.size(); i++) {
    const SpellInfo& ci = GetSpellInfo(tempid[i]);
    if (last_head == ci.name.data[0]) continue;
    last_head = ci.name.data[0];
    ids.Add(tempid[i]);
  }
  return true;
}
int SpellManager::GetSpellStringWithContext(wchar_t code, wchar_t prev,
                                            wchar_t next, wstring& spell) {
  tSpellIds id;
  if (GetSpellIdEx(code, id, true, prev, next)) {
    for (int i = 0; i < id.size(); i++) {
      const SpellInfo& si = GetSpellInfo(id.at(i));
      if (i > 0) spell += L",";
      spell += wstring(si.name.data, si.len);
    }
  }
  return id.size();
}
int SpellManager::GetSpellString(wchar_t code, wstring& spell) {
  tSpellIds id;
  if (GetSpellId(code, id)) {
    for (int i = 0; i < id.size(); i++) {
      const SpellInfo& si = GetSpellInfo(id.at(i));
      if (i > 0) spell += L",";
      spell += wstring(si.name.data, si.len);
    }
  }
  return id.size();
}

//--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
SpellBufBuilderImpl::SpellBufBuilderImpl(wchar_t* buf, int capcity)
    : m_capcity(capcity), m_buffer(buf), m_count(0), m_length(0) {
  memset(m_buffer, 0, m_capcity);
}
void SpellBufBuilderImpl::Reset() {
  memset(m_buffer, 0, m_capcity);
  m_count = 0;
  m_length = 0;
}

void SpellBufBuilderImpl::AddStart(wchar_t c) {
  m_count++;
  if (m_count > 1) AddString((const wchar_t*)&c, 1);
}

void SpellBufBuilderImpl::AddString(wchar_t str) {
  if (1 + m_length + 1 <= m_capcity) {
    m_buffer[m_length] = str;
    m_length++;
    m_buffer[m_length] = '\0';
  }
}
void SpellBufBuilderImpl::AddString(const wchar_t* str, int len) {
  if (len + m_length + 1 <= m_capcity && len > 0 && str) {
    memcpy(m_buffer + m_length, str, len * sizeof(wchar_t));
    m_length += len;
    m_buffer[m_length] = '\0';
  }
}

int SpellManager::UnitTest() {
  BaseString fname =
      "D:\\Sumscope\\QB\\dev\\quoteboard\\projects\\Pinyin\\utf8_to_hanyu_"
      "pinyin2.txt";
  SpellManager spellmgr;
  {
    BS_METRICS("laod dict");

    BaseString f1 =
        "D:\\Sumscope\\QB\\dev\\quoteboard\\projects\\Pinyin\\utf8_to_hanyu_"
        "pinyin_wchar.txt";
    BaseString f2 =
        "D:\\Sumscope\\QB\\dev\\quoteboard\\projects\\Pinyin\\utf8_to_hanyu_"
        "pinyin_wchar2.txt";
    // spellmgr.LoadDict(fname);
    // spellmgr.ExportDict(f1);
    spellmgr.LoadDefaultDict();
    spellmgr.ExportDict(f2);
    return 0;
  }

  if (0) {
    // 验证IsMatchPinyin正确性
    HighResTime timer;
    bool bmatch = false;
    int error = 0;
    for (int i = 0; i < 1; i++) {
      bmatch = spellmgr.IsMatchPinyin(L"出7-14天1.5亿  押利率  限银行农信", 0,
                                      L"yinhangn", 0, true, false);
      if (!bmatch) error++;
    }
    BVIEW("times:%d bmatch:%s  pos:%d  error:%d last:%d", 10000,
          bmatch ? "" : "", spellmgr.GetLastMatchPos(), error,
          timer.elapsed().microsec());

    return 0;
  }

  if (0) {
    // 验证生成拼音字符串的正确性
    wstring wstr = L"出7-14天1.5亿  押利率  限银行农信";
    wstring shortspell, fullspell;
    {
      BS_METRICS("GetAllSpellForString");
      // for (int i = 0; i < 1000;i++)
      spellmgr.GetAllSpellForString(wstr, shortspell, fullspell);
    }
  }

  // 以下代码用于扫描提供的文本中的多音字以及该多音字的前后字符最多的情况
  vector<BaseString> lines;
  std::vector<wstring> wstrlines;
  map<wchar_t, int> polycountmap;
  struct polycount {
    wchar_t code;
    int count;
    bool operator<(const polycount& p1) const {
      if (count == p1.count) return code > p1.code;
      return count > p1.count;
    }
  };
  struct PolyContext {
    wchar_t code;
    int count;
    map<wchar_t, int> prefixmap;
    map<wchar_t, int> postmap;

   public:
    BaseString GetString(SpellManager& spellmgr) {
      wstring prefix = GetContextString(prefixmap);
      wstring post = GetContextString(postmap);
      wstring spell;
      spellmgr.GetSpellString(code, spell);
      return os::fmtstr("%s  %6d\tspell:%20s\tprefix:%4d-%40s  post:%4d-%40s  ",
                        W2G(&code, 1).c_str(), count,
                        W2G(spell.c_str(), spell.size()).c_str(),
                        prefixmap.size(),
                        W2G(prefix.c_str(), prefix.size()).c_str(),
                        postmap.size(), W2G(post.c_str(), post.size()).c_str());
    }
    wstring GetContextString(map<wchar_t, int>& ppmap) {
      std::set<polycount> polycountset;
      for (std::map<wchar_t, int>::iterator it = ppmap.begin();
           it != ppmap.end(); ++it) {
        polycount pc;
        pc.code = it->first;
        pc.count = it->second;
        polycountset.insert(pc);
      }

      wstring wstr;
      for (std::set<polycount>::iterator it = polycountset.begin();
           it != polycountset.end(); ++it) {
        if (wstr.size() >= 36) {
          wstr.push_back(L'.');
          wstr.push_back(L'.');
          wstr.push_back(L'.');
          break;
        }
        wstr.push_back(it->code);
      }
      return wstr;
    }
    void AddContext(SpellManager& spellmgr, const wstring& str) {
      for (int i = 0; i < (int)str.size(); i++) {
        if (str[i] != code) continue;
        if (i - 1 >= 0) AddContext(spellmgr, str[i - 1], true);
        if (i + 1 < (int)str.size()) AddContext(spellmgr, str[i + 1], false);
      }
    }
    void AddContext(SpellManager& spellmgr, wchar_t c, bool prefix) {
      if (c == 0) return;
      SpellManager::tSpellIds id;
      if (!spellmgr.GetSpellId(c, id)) return;
      map<wchar_t, int>* pmap = prefix ? &prefixmap : &postmap;
      map<wchar_t, int>::iterator it = pmap->find(c);
      if (it == pmap->end())
        pmap->insert(map<wchar_t, int>::value_type(c, 1));
      else
        it->second = it->second + 1;
    }
  };

  map<wchar_t, PolyContext> polycontextmap;
  // StringUtil::LoadLinesFromFile("E:\\temp\\memo.txt", lines);//utf8 27-end
  StringUtil::LoadLinesFromFile(L"E:\\temp\\pinyin.txt",
                                lines);  // gbk name|short|full
  for (int i = 0; i < (int)lines.size(); i++) {
    // wstring wstr = U2W(lines[i].substr(27, 1000));
    strinfo tokens[128];
    int tknum = StringUtil::SplitStringInfo(lines[i].c_str(), lines[i].size(),
                                            "|", tokens, 128);
    if (tknum < 3) continue;

    std::wstring wstr =
        G2W(lines[i].c_str() + tokens[0].pos, tokens[0].len).c_str();
    std::wstring polyc;
    wstrlines.push_back(wstr);
    spellmgr.GetPolySpell(wstr, polyc);
    for (int j = 0; j < (int)polyc.size(); j++) {
      map<wchar_t, int>::iterator it = polycountmap.find(polyc[j]);
      if (it != polycountmap.end()) {
        it->second = it->second + 1;
      } else
        polycountmap.insert(map<wchar_t, int>::value_type(polyc[j], 1));

      map<wchar_t, PolyContext>::iterator pit = polycontextmap.find(polyc[j]);
      if (pit != polycontextmap.end()) {
        PolyContext& pc = pit->second;
        pc.count++;
        pc.AddContext(spellmgr, wstr);
      } else {
        PolyContext pc;
        pc.code = polyc[j];
        pc.count = 1;
        pc.AddContext(spellmgr, wstr);
        polycontextmap.insert(
            map<wchar_t, PolyContext>::value_type(polyc[j], pc));
      }
    }
  }

  if (0) {
    HighResTime timer;
    for (int i = 0; i < (int)wstrlines.size(); i++) {
      wstring& wstr = wstrlines[i];
      bool bmatch = spellmgr.IsMatchPinyin(wstr.c_str(), wstr.size(),
                                           L"yinhangn", 0, true, false);
    }
    BVIEW("counts:%d last:%d", wstrlines.size(), timer.elapsed().microsec());
    return 0;
  }

  // 造成构造时间过长的主要有那些多音字
  std::set<polycount> polycountset;
  for (std::map<wchar_t, int>::iterator it = polycountmap.begin();
       it != polycountmap.end(); ++it) {
    polycount pc;
    pc.code = it->first;
    pc.count = it->second;
    polycountset.insert(pc);
  }
  for (std::set<polycount>::iterator it = polycountset.begin();
       it != polycountset.end(); ++it) {
    BVIEW("\t%s %6d  ", W2G(&it->code, 1).c_str(), it->count);
  }
  for (std::set<polycount>::iterator it = polycountset.begin();
       it != polycountset.end(); ++it) {
    map<wchar_t, PolyContext>::iterator pit = polycontextmap.find(it->code);
    if (pit == polycontextmap.end()) continue;
    PolyContext& pc = pit->second;
    BVIEW("%s", pc.GetString(spellmgr).c_str());
  }
  // 这些多音字前后有那些常用字
  return 0;
}

namespace {
class SpellManagerInst {
 public:
  static CSLock& GetLock() {
    static CSLock lock;
    return lock;
  }
  static qb::base::SpellManager& GetManager() {
    static bool init = false;
    static qb::base::SpellManager pm;
    if (!init) {
      SCOPED_LOCK(GetLock());
      if (!init) {
        init = true;
        pm.LoadDefaultDict();
      }
    }
    return pm;
  }
};
}  // namespace
void Spell::ParseEx(const unsigned short* buffer, int len,
                    unsigned short* quanbuf, int& quanlen,
                    unsigned short* jianbuf, int& jianlen) {
  SCOPED_LOCK(SpellManagerInst::GetLock());
  SpellManagerInst::GetManager().GetSpell((const wchar_t*)buffer, len,
                                          (wchar_t*)quanbuf, quanlen,
                                          (wchar_t*)jianbuf, jianlen);
}
bool Spell::IsAlignMatchPinyin(const unsigned short* text, int tlen,
                               const unsigned short* key, int klen) {
  SCOPED_LOCK(SpellManagerInst::GetLock());
  return SpellManagerInst::GetManager().IsAlignMatchPinyin(
      (const wchar_t*)text, tlen, (const wchar_t*)key, klen);
}
bool Spell::IsMatchPinyin(const wchar_t* text, int tlen, const wchar_t* key,
                          int klen, bool full, bool checkcn) {
  SCOPED_LOCK(SpellManagerInst::GetLock());
  return SpellManagerInst::GetManager().IsMatchPinyin(
      (const wchar_t*)text, tlen, (const wchar_t*)key, klen, full, checkcn);
}
//		static_assert(sizeof(unsigned short) == sizeof(wchar_t),"wchar_t
// must equal to unsigned short");
void Spell::Parse(const wchar_t* buffer, int len, wchar_t* quanbuf,
                  int& quanlen, wchar_t* jianbuf, int& jianlen) {
  Spell::ParseEx((const unsigned short*)buffer, len, (unsigned short*)quanbuf,
                 quanlen, (unsigned short*)jianbuf, jianlen);
}

}  // namespace base
}  // namespace qb
